package com.loit.loitcommon.utils;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * redicache 工具类
 * 
 */
//@Component
//@SuppressWarnings({"unchecked","rawtypes"})
public class RedisUtil {
	
	private static RedisTemplate redisTemplate;
	@Resource
	public void setRedisTemplate(RedisTemplate redisTemplate){
		//用于解决模糊匹配时无法匹配全部的key值，故把key去掉序列化
		RedisSerializer stringSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(stringSerializer);
//		redisTemplate.setValueSerializer(stringSerializer);
//		redisTemplate.setHashKeySerializer(stringSerializer);
//		redisTemplate.setHashValueSerializer(stringSerializer);
//		RedisUtil.redisTemplate = redisTemplate;
		this.redisTemplate = redisTemplate;
	}
	
	/**
	 * 批量删除对应的value
	 * 
	 * @param keys
	 */
	public static void remove(final String... keys) {
		for (String key : keys) {
			remove(key);
		}
	}
	/**
	 * 批量删除key
	 * 
	 * @param pattern
	 */
	public static void removePattern(final String pattern) {
		//keys的操作会导致数据库暂时被锁住，其他的请求都会被堵塞；业务量大的时候会出问题，故采用scan 进行替换
//		Set<Serializable> keys = redisTemplate.keys("*"+pattern+"*");
		List<String> keys = keys("*"+pattern+"*");
		if (keys.size() > 0)
		redisTemplate.delete(keys);
	}


	/**
	 * scan 实现
	 * @param pattern   表达式
	 * @param consumer  对迭代到的key进行操作
	 */
	public static void scan(String pattern, Consumer<byte[]> consumer) {
		redisTemplate.execute((RedisConnection connection) -> {
			try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
				cursor.forEachRemaining(consumer);
				return null;
			} catch (IOException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		});
	}

	/**
	 * 获取符合条件的key
	 * @param pattern   表达式
	 * @return
	 */
	public static List<String> keys(String pattern) {
		List<String> keys = new ArrayList<>();
		scan(pattern, item -> {
			//符合条件的key
			String key = new String(item, StandardCharsets.UTF_8);
			keys.add(key);
		});
		return keys;
	}

	/**
	 * 删除对应的value
	 * 
	 * @param key
	 */
	public static void remove(final String key) {
		if (exists(key)) {
			redisTemplate.delete(key);
		}
	}
	/**
	 * 判断缓存中是否有对应的value
	 * 
	 * @param key
	 * @return
	 */
	public static boolean exists(final String key) {
		return redisTemplate.hasKey(key);
	}
	/**
	 * 读取缓存
	 * 
	 * @param key
	 * @return
	 */
	public static Object get(final String key) {
		Object result = null;
		ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
		result = operations.get(key);
		return result;
	}
	/**
	 * 写入缓存
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public static boolean set(final String key, Object value) {
		boolean result = false;
		try {
			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	/**
	 * 写入缓存
	 * 有效期
	 * @param key
	 * @param value
	 * @param expireTime 时效，单位：秒
	 * @return
	 */
	public static boolean set(final String key, Object value, Long expireTime) {
		boolean result = false;
		try {
			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			if(expireTime!=0L)
				redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 写入缓存（不重置过期时间）
	 * @param key
	 * @param value
	 * @return
	 */
	public static boolean setWithoutExpireTime(final String key, Object value) {
		boolean result = false;
		try {
			Long expireTime = redisTemplate.getExpire(key);
			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			if(expireTime!=0L) {
				Date date = new Date(Instant.now().toEpochMilli() + expireTime * 1000);
				redisTemplate.expireAt(key, date);
				System.out.println(redisTemplate.getExpire(key));
			}
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * @desc 向集合中添加一个元素,从左到右
	 * @param key
	 * @param value
	 * @author yangwenbin
	 */
	public static boolean lpush(final String key,Object value){
		boolean result = false;
		try {
			ListOperations<Serializable, Object> operations = redisTemplate.opsForList();
			operations.leftPush(key,value);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * @desc 向集合中添加一个元素,从左到右,有效期
	 * @param key
	 * @param value
	 * @param expireTime 时效 单位：秒
	 * @author yangwenbin
	 */
	public static boolean lpush(final String key,Object value,Long expireTime){
		boolean result = false;
		try {
			ListOperations<Serializable, Object> operations = redisTemplate.opsForList();
			operations.leftPush(key,value);
			redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * @desc 方法描述
	 * @param key
	 * @return List集合
	 * @author yangwenbin
	 * @date
	 */
	public static List<Object> getList(final String key){
		List<Object> list=null;
		try {
			ListOperations<Serializable, Object> operations = redisTemplate.opsForList();
			list = operations.range(key,0,-1);

		}catch (Exception e){
			e.printStackTrace();
		}
		return list;
	}

	/**
	 * Redis 序列化使用json方式
	 * Demon add by 2020-07-07
	 * @param factory
	 * @return
	 */
//	@Bean(name = "redisTemplate")
	public RedisTemplate<String, String> strRedisTemplate(RedisConnectionFactory factory) {
		RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(factory);

		// 使用Jackson2JsonRedisSerialize 替换默认序列化(默认采用的是JDK序列化)
		Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);

		redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
		redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
		return redisTemplate;
	}
}
